#include "myleds.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
// myleds{
//     compatible = "hqyj,leds";
//     core_leds {
//      led1 = <&gpioz 5 0>;
//     	led2 = <&gpioz 6 0>;
//     	led3 = <&gpioz 7 0>;
//     };
//     extend_leds{
//     	led1 = <&gpioe 10 0>;
//     	led2 = <&gpiof 10 0>;
//     	led3 = <&gpioe 8 0>;
//     };
// };
#define CNAME "myleds"
#define COUNT 1
struct device_node *node, *cnode, *enode;
int gpiono[6] = { 0 };
char* name[] = { "led1", "led2", "led3" };
int major;
struct class* cls;
struct device* dev;
int myleds_open(struct inode* inode, struct file* filp)
{
    return 0;
}

long myleds_ioctl(struct file* filp,
    unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
    case CORE_LED_ON:
        gpio_set_value(gpiono[arg], 1);
        break;
    case CORE_LED_OFF:
        gpio_set_value(gpiono[arg], 0);
        break;
    case EXTEND_LED_ON:
        gpio_set_value(gpiono[arg+3], 1);
        break;
    case EXTEND_LED_OFF:
        gpio_set_value(gpiono[arg+3], 0);
        break;
    }
    return 0;
}
int myleds_close(struct inode* inode, struct file* filp)
{
    return 0;
}
struct file_operations fops = {
    .open = myleds_open,
    .unlocked_ioctl = myleds_ioctl,
    .release = myleds_close,
};
static int __init myleds_init(void)
{
    int i, ret;
    // 1.获取节点
    node = of_find_compatible_node(NULL, NULL, "hqyj,leds");
    if (node == NULL) {
        printk("get node error\n");
        return -ENODATA;
    }
    cnode = of_get_child_by_name(node, "core_leds");
    if (cnode == NULL) {
        printk("get cnode error\n");
        return -ENODATA;
    }
    enode = of_get_child_by_name(node, "extend_leds");
    if (enode == NULL) {
        printk("get enode error\n");
        return -ENODATA;
    }
    // 2.解析属性
    for (i = 0; i < ARRAY_SIZE(name); i++) {
        gpiono[i] = of_get_named_gpio(cnode, name[i], 0);
        if (gpiono[i] < 0) {
            printk("get property error\n");
            return gpiono[i];
        }
        // 3.初始化led
        ret = gpio_request(gpiono[i], NULL);
        if (ret) {
            printk("request gpio error\n");
            return ret;
        }
        gpio_direction_output(gpiono[i], 0);

        gpiono[i + 3] = of_get_named_gpio(enode, name[i], 0);
        if (gpiono[i + 3] < 0) {
            printk("get property error\n");
            return gpiono[i + 3];
        }
        // 3.初始化led
        ret = gpio_request(gpiono[i + 3], NULL);
        if (ret) {
            printk("request gpio error\n");
            return ret;
        }
        gpio_direction_output(gpiono[i + 3], 0);
    }

    // 4.注册字符设备驱动
    major = register_chrdev(0, CNAME, &fops);
    if (major < 0) {
        printk("register char device driver error\n");
        return major;
    }

    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls)) {
        printk("class create error\n");
        return PTR_ERR(cls);
    }
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);
    if (IS_ERR(dev)) {
        printk("device create error\n");
        return PTR_ERR(dev);
    }
    return 0;
}
static void __exit myleds_exit(void)
{
    int i;
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, CNAME);
    for (i = 0; i < ARRAY_SIZE(gpiono); i++) {
        gpio_free(gpiono[i]);
    }
}
module_init(myleds_init);
module_exit(myleds_exit);
MODULE_LICENSE("GPL");